(require 'mal/types)

(defun pr-str (form print-readably)
  (let (value)
    (cond
     ((eq mal-nil form)
      "nil")
     ((eq mal-true form)
      "true")
     ((eq mal-false form)
      "false")
     ((setq value (mal-number-value form))
      (number-to-string value))
     ((setq value (mal-string-value form))
      (if print-readably
          (let ((print-escape-newlines t))
            (prin1-to-string value))
        value))
     ((setq value (mal-symbol-value form))
      (symbol-name value))
     ((setq value (mal-keyword-value form))
      value)
     ((setq value (mal-list-value form))
      (pr-list value print-readably))
     ((mal-list-p form)
      "()")
     ((setq value (mal-vector-value form))
      (pr-vector value print-readably))
     ((setq value (mal-map-value form))
      (pr-map value print-readably))
     ((or (mal-fn-core-value form) (mal-func-value form))
      "#<function>")
     ((mal-macro-value form)
      "#<macro>")
     ((setq value (mal-atom-value form))
      (format "(atom %s)" (pr-str value print-readably)))
     (t (error "pr-str: unknown type: %s" form)))))

(defun pr-list (form print-readably)
  (let ((items (pr-join form print-readably " ")))
    (concat "(" items ")")))

(defun pr-vector (form print-readably)
  (let ((items (pr-join form print-readably " ")))
    (concat "[" items "]")))

(defun pr-map (form print-readably)
  (let (pairs)
    (maphash
     (lambda (key value)
       (push value pairs)
       (push key pairs))
     form)
    (let ((items (pr-join pairs print-readably " ")))
      (concat "{" items "}"))))

(defun pr-join (forms print-readably separator)
  (mapconcat (lambda (item) (pr-str item print-readably)) forms separator))

(provide 'mal/printer)
